use auth::{oauth2::OAuth2Config, store::AuthStoreConfig};
use axum_server::tls_rustls::RustlsConfig;
use tracing::info;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use serde_derive::Deserialize;

use crate::app::{App, DataDrivenPagesConfig, AppSessionConfig};

#[derive(Deserialize, Debug, Default)]
pub struct TlsConfig {
    cert: String,
    key: String,
}

fn default_site_root() -> String { "./site-root".to_string() }
fn default_bind_addr() -> String { "0.0.0.0:3030".to_string() }

#[derive(Deserialize, Debug)]
pub struct AppConfig {
    #[serde(default="default_site_root")]
    site_root: String,
    #[serde(default="default_bind_addr")]
    bind_addr: String,
    tls: Option<TlsConfig>,
    datadriven: DataDrivenPagesConfig,
    pagedata: toml::Value,
    session: AppSessionConfig,
    oauth2: OAuth2Config,
    auth_store: AuthStoreConfig,
}

pub(crate) async fn bootstrap() -> App {
    use std::env::var;

    // load the environment form '.env' files
    dotenv::dotenv().ok();

    // load the config from the configfile
    let configfile = var("CONFIG_FILE").ok().unwrap_or_else(|| "config.toml".to_owned());
    let cf = std::fs::read_to_string(&configfile).expect("could not open config file");
    let mut config: AppConfig = toml::from_str(&cf).expect("could not parse config file");

    // patch the config using env vars
    if let Some(val) = var("SITE_ROOT").ok() { config.site_root = val; }
    if let Some(val) = var("BIND_ADDR").ok() { config.bind_addr = val; }
    if let Some(val) = var("TLS_CERT").ok() { config.tls.get_or_insert(Default::default()).cert = val; }
    if let Some(val) = var("TLS_KEY").ok() { config.tls.get_or_insert(Default::default()).key = val; }
    if let Some(_) = var("DISABLE_TLS").ok() { config.tls = None; }
    if let Some(val) = var("COOKIE_DOMAIN").ok() { config.session.cookie_domain = val }

    let ansi = var("COLOR").ok().and_then(|s| s.parse().ok()).unwrap_or(true);

    tracing_subscriber::registry()
        .with(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "info,edge::app=debug,tower_http=debug".into()),
        )
        .with(tracing_subscriber::fmt::layer().with_ansi(ansi))
        .init();

    info!("startup");

    let root = std::path::PathBuf::from(config.site_root);

    let tls_config = if let Some(TlsConfig { cert, key }) = config.tls {
        Some(RustlsConfig::from_pem_file(cert, key).await.unwrap())
    } else {
        None
    };
    if !config.oauth2.client_id.is_empty() || !config.oauth2.client_secret.is_empty() {
        panic!("oauth2 credentials (oauth2.client_id, oauth2.client_secret) must not be set in the config file")
    }

    let oauth2_config = config.oauth2.update_from_environment().unwrap();

    App::new(
        root, 
        config.pagedata, 
        config.datadriven, 
        config.bind_addr, 
        tls_config, 
        config.session,
        oauth2_config,
        config.auth_store,
)
}

